package tuan.core.extract;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import tuan.filter.*;
import tuan.score.*;
import tuan.travel.*;
import tuan.utils.*;

public class ProductExtract {
	Node root = null;
	Node titleNode = null;
	Node contentNode = null;
	Node imgNode = null;
	Product product; 

	public ProductExtract(Product product, Node root)
			throws Exception {
		if (root == null || product == null || product.url == null) {
			throw new Exception("null arguments");
		}
		this.root = root;
		this.product = product;
	}

	/**
	 * 获取商品信息
	 * 
	 * @return
	 * @throws Exception
	 */
	public Product extract() throws Exception {
		TravelFilterChain travelChain = new TravelFilterChain();
		travelChain.addFilter(new AllowdTagFilter(new String[] { "#text" }));
		travelChain.addFilter(new NotAllowdParentTagFilter(new String[] { "TITLE",
				"SCRIPT",
				"LI" }));
		travelChain.addFilter(new PureTextLengthFiltter(10, ">"));
		DomUtil.postOrderTree(root, root, null, travelChain);
		List<Node> textNodeList = travelChain.getNodeList();

		NodeScoreChain nodeChain = new NodeScoreChain();
		nodeChain.addScore(new PureLengthScore(ConfigConst.GE_LEN, ">=", ConfigConst.GE_LEN_SCORE));
		nodeChain.addScore(new EqualPreTagScore(ConfigConst.tagRules));
		nodeChain.addScore(new MatchTextScore(ConfigConst.textRules));
		nodeChain.addScore(new FontSizeScore(ConfigConst.FONT_MIN_SIZE, ">=", ConfigConst.FONT_MIN_SIZE_SCORE));
		nodeChain.setScore(textNodeList);

		//printScore(textNodeList);
		titleNode = NodeUtil.getMaxScoreNode(textNodeList);
		if (titleNode != null) {
			if (product.title.length() < titleNode.getTextContent().trim().length()) {
				product.title = titleNode.getTextContent().trim();
			}
			setContent();
			setPrice();
			setImg();
			setStatus();
			postExtract();
			setImgList();
			setAddrList();
		}

		return product;

	}

	/**
	 * 设置正文
	 */
	void setContent() {
		try {
			Node sNode = root;
			if (titleNode != null) {
				sNode = NodeUtil.getNextNode(titleNode);
			}
			TravelFilterChain travelChain = new TravelFilterChain();
			travelChain.addFilter(new AllowdTagFilter(new String[] { "#text" }));
			travelChain.addFilter(new NotAllowdParentTagFilter(new String[] { "TITLE",
					"A",
					"SCRIPT",
					"LI",
					"STYLE",
					"COMMENT" }));
			travelChain.addFilter(new TextLengthFiltter(10, ">"));
			DomUtil.postOrderTree(root, sNode, null, travelChain);
			List<Node> textNodeList = travelChain.getNodeList();

			DomUtil.setTextNodeFont(textNodeList);
			Object maxAttrValue = DomUtil.getMaxTextAttr(textNodeList, "style");

			if (maxAttrValue == null) {
				return;
			}
			NodeListFilterChain nodeListChain = new NodeListFilterChain();

			nodeListChain.addFilter(new AllowdAttrFilter("style", maxAttrValue));
			textNodeList = nodeListChain.filter(textNodeList);
			if (textNodeList.size() == 0) {
				return;
			}
			contentNode = DomUtil.getMinComParrent(textNodeList);
			if (titleNode != null) {
				FindNodeTravel findTravel = new FindNodeTravel(contentNode);
				DomUtil.postOrderTree(contentNode, contentNode, null, findTravel);
				if (!findTravel.found) {
					return;
				}
			}

			//设置内容
			product.content = NodeUtil.getContent(contentNode);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 设置价格
	 */
	void setPrice() {
		try {
			Node sNode = root;
			Node eNode = null;
			if (titleNode != null) {
				sNode = NodeUtil.getNextNode(titleNode);
			}
			if (contentNode != null) {
				eNode = contentNode;
			}
			for (int step = 0; step < 2; step++) {
				TravelFilterChain travelChain = new TravelFilterChain();
				travelChain.addFilter(new AllowdTagFilter(new String[] { "#text" }));
				travelChain.addFilter(new NotAllowdParentTagFilter(new String[] { "TITLE",
						"A",
						"SCRIPT",
						"STYLE" }));
				//travelChain.addFilter(new TextLengthFiltter(10, "<"));
				DomUtil.postOrderTree(root, sNode, eNode, travelChain);
				List<Node> textNodeList = travelChain.getNodeList();

				textNodeList = NodeUtil.ReOrderNodeByTable(textNodeList);
				String text = NodeUtil.getText(textNodeList);

				String regPattern = "(\\d+)\\s*[^0-9]{0,3}购买";
				String retstr = RegUtil.getGroup1(regPattern, text);
				if (retstr != null && product.buyNum.length() == 0) {
					product.buyNum = retstr;
					text = RegUtil.deleteGroup1(regPattern, text);
				}

				//order
				int orderOld = Math.max(text.indexOf("市场价"), text.indexOf("原价"));
				int orderNow = text.indexOf("现价");
				int orderSave = text.indexOf("节省");
				int orderDiscount = text.indexOf("折扣");

				List<Integer> orderList = Arrays.asList(orderOld, orderNow, orderSave, orderDiscount);
				Collections.sort(orderList);

				for (int i : orderList) {
					if (i == orderOld && product.oldPrice.length() == 0) {
						regPattern = "原价[^0-9]{0,20}(\\d+(\\.\\d+)?)";
						retstr = RegUtil.getGroup1(regPattern, text);
						if (retstr != null) {
							product.oldPrice = retstr;
							text = RegUtil.deleteGroup1(regPattern, text);
						} else {
							regPattern = "市场价[^0-9]{0,20}(\\d+(\\.\\d+)?)";
							retstr = RegUtil.getGroup1(regPattern, text);
							text = RegUtil.deleteGroup1(regPattern, text);
							if (retstr != null) {
								product.oldPrice = retstr;
								text = RegUtil.deleteGroup1(regPattern, text);
							}
						}
					} else if (i == orderNow && product.nowPrice.length() == 0) {
						regPattern = "现价[^0-9]{0,20}(\\d+(\\.\\d+)?)";
						retstr = RegUtil.getGroup1(regPattern, text);
						if (retstr != null) {
							product.nowPrice = retstr;
							text = RegUtil.deleteGroup1(regPattern, text);
						}
					} else if (i == orderSave && product.savePrice.length() == 0) {
						regPattern = "节省[^0-9]{0,20}(\\d+(\\.\\d+)?)";
						retstr = RegUtil.getGroup1(regPattern, text);
						if (retstr != null) {
							product.savePrice = retstr;
							text = RegUtil.deleteGroup1(regPattern, text);
						}
					} else if (i == orderDiscount && product.discount.length() == 0) {
						regPattern = "折扣[^0-9]{0,20}(\\d+(\\.\\d+)?)";
						retstr = RegUtil.getGroup1(regPattern, text);
						if (retstr != null) {
							product.discount = retstr;
							text = RegUtil.deleteGroup1(regPattern, text);
						}
					}
				}
				if (product.nowPrice.length() > 0 && product.savePrice.length() > 0 && product.oldPrice.length() > 0 && product.discount.length() > 0) {
					break;
				} else {
					eNode = null;
				}
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 设置商品图片
	 */
	void setImg() {
		try {
			Node sNode = root;
			Node eNode = null;
			if (titleNode != null) {
				sNode = NodeUtil.getNextNode(titleNode);
			}
			//if (contentNode != null) {
				//eNode = contentNode;
			//}

			TravelFilterChain travelChain = new TravelFilterChain();
			travelChain.addFilter(new AllowdTagFilter(new String[] { "IMG" }));
			travelChain.addFilter(new NotAllowdParentTagFilter(new String[] { "TITLE",
					"SCRIPT" }));
			DomUtil.postOrderTree(root, sNode, eNode, travelChain);
			List<Node> nodeList = travelChain.getNodeList();

			NodeScoreChain nodeChain = new NodeScoreChain();
			nodeChain.addScore(new NumDirImgUrlScore(ConfigConst.HAS_NUM_DIR_SCORE));
			nodeChain.addScore(new ImgSizeScore("height", ConfigConst.IMG_MIN_SIZE_HEIGHT, ">=", ConfigConst.IMG_MIN_SIZE_HEIGHT_SCORE));
			nodeChain.addScore(new ImgSizeScore("width", ConfigConst.IMG_MIN_SIZE_WIDTH, ">=", ConfigConst.IMG_MIN_SIZE_WIDTH_SCORE));
			nodeChain.setScore(nodeList);

			imgNode = NodeUtil.getMaxScoreNode(nodeList);
			if (imgNode != null) {
				if (imgNode.hasAttributes()) {
					NamedNodeMap attrMap = imgNode.getAttributes();
					if (attrMap.getNamedItem("src") != null) {
						product.imgUrl = StringUtil.getFullUrl(product.url, attrMap.getNamedItem("src").getNodeValue());
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取是否团购结束
	 */
	void setStatus() {
		try {
			Node sNode = root;
			Node eNode = null;
			if (titleNode != null) {
				sNode = NodeUtil.getNextNode(titleNode);
			}
			if (contentNode != null) {
				eNode = contentNode;
			}

			TravelFilterChain travelChain = new TravelFilterChain();
			travelChain.addFilter(new AllowdTagFilter(new String[] { "#text" }));
			travelChain.addFilter(new NotAllowdParentTagFilter(new String[] { "TITLE",
					"A",
					"SCRIPT" }));
			//travelChain.addFilter(new TextLengthFiltter(10, "<"));
			DomUtil.postOrderTree(root, sNode, eNode, travelChain);
			List<Node> textNodeList = travelChain.getNodeList();

			String text = NodeUtil.getText(textNodeList);
			if (RegUtil.match("团购\\.?\\.?结束|卖完|卖光", text)) {
				product.isEnd = true;
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 计算可计算属性值
	 */
	void postExtract() {
		String now = product.nowPrice;
		String old = product.oldPrice;
		String save = product.savePrice;
		String discount = product.discount;
		//未有属性缺失
		if (!now.equals("") && !old.equals("") && !save.equals("") && !discount.equals("")) {
			return;
		}

		int valueCount = 0;
		if (!now.equals("")) {
			valueCount++;
		}
		if (!old.equals("")) {
			valueCount++;
		}
		if (!save.equals("")) {
			valueCount++;
		}
		if (!discount.equals("")) {
			valueCount++;
		}
		//节省和折扣等价,优先使用节省
		if (!discount.equals("") && !save.equals("")) {
			valueCount--;
		}
		//少于2个属性，无法计算
		if (valueCount < 2) {
			return;
		}
		//now = old-save | old * (discount * 0.1)
		if (now.equals("")) {
			if (!save.equals("")) {
				now = String.format("%f", Double.parseDouble(old) - Double.parseDouble(save));
			} else {
				now = String.format("%f", Double.parseDouble(old) * Double.parseDouble(discount) * 0.1);
			}
		}
		//old = now+save | now / (discount * 0.1)
		if (old.equals("")) {
			if (!save.equals("")) {
				old = String.format("%f", Double.parseDouble(now) + Double.parseDouble(save));
			} else {
				if (Double.parseDouble(discount) * 10000 > 0.1) {
					old = String.format("%f", Double.parseDouble(now) / Double.parseDouble(discount) * 10);
				}
			}
		}
		//save = old-now
		if (save.equals("")) {
			save = String.format("%f", Double.parseDouble(old) - Double.parseDouble(now));
		}
		//discount = now / old * 10
		if (discount.equals("")) {
			if (Double.parseDouble(old) * 10000 > 0.1) {
				discount = String.format("%f", Double.parseDouble(now) / Double.parseDouble(old) * 10);
			}
		}

		//异常容错
		if (Double.parseDouble(old) < Double.parseDouble(now)) {
			product.oldPrice = "";
			product.nowPrice = "";
			product.savePrice = "";
			product.discount = "";
			product.fetchOk = false;
			return;
		}
		product.oldPrice = old;
		product.nowPrice = now;
		product.savePrice = save;
		product.discount = discount;
	}

	/**
	 * 设置正文中的图片
	 */
	void setImgList() {
		try {
			if (contentNode == null) {
				return;
			}

			TravelFilterChain travelChain = new TravelFilterChain();
			travelChain.addFilter(new AllowdTagFilter(new String[] { "IMG" }));
			DomUtil.postOrderTree(contentNode, contentNode, null, travelChain);
			List<Node> nodeList = travelChain.getNodeList();

			Iterator<Node> iter = nodeList.iterator();
			while (iter.hasNext()) {
				Node node = iter.next();
				if (node.hasAttributes()) {
					NamedNodeMap attrMap = node.getAttributes();
					if (attrMap.getNamedItem("src") != null) {
						product.imgList.add(StringUtil.getFullUrl(product.url, attrMap.getNamedItem("src").getNodeValue()));
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	void setAddrList() {
		try {
			Node sNode = root;
			if (titleNode != null) {
				sNode = titleNode;
			}
			TravelFilterChain travelChain = new TravelFilterChain();
			travelChain.addFilter(new AllowdTagFilter(new String[] { "#text" }));
			travelChain.addFilter(new NotAllowdParentTagFilter(new String[] { "TITLE",
					"A",
					"SCRIPT",
					"STYLE",
					"COMMENT" }));
			travelChain.addFilter(new TextLengthFiltter(10, ">"));
			DomUtil.postOrderTree(root, sNode, null, travelChain);
			List<Node> textNodeList = travelChain.getNodeList();

			String text = NodeUtil.getText(textNodeList);
			String regStr = ConfigConst.addrPattern;
			Pattern pattern = RegUtil.getPattern(regStr);
			Matcher m = pattern.matcher(text);
			while (m.find()) {
				product.addrList.add(m.group(1));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	class FindNodeTravel implements TravelCallback {
		boolean found = false;
		Node findNode;

		public FindNodeTravel(Node findNode) throws Exception {
			if (findNode == null) {
				throw new Exception("null arguments");
			}
			this.findNode = findNode;
		}

		public void callback(Node root, Node node) throws Exception {
			// TODO Auto-generated method stub
			if (findNode.equals(node)) {
				found = true;
			}
		}
	}
}
